﻿using Currency.Infra.IRepository;
using DotNetCore.CAP;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System;
using System.Data;
using System.Threading;
using System.Threading.Tasks;

namespace Currency.Infra.Repository
{
    /// <summary>
    /// 事物
    /// </summary>
    /// <typeparam name="TDbContext"></typeparam>
    public sealed class UnitOfWork<TDbContext> : IUnitOfWork where TDbContext : DbContext
    {
        private readonly TDbContext _dbContext;
        private readonly ICapPublisher _capPublisher;
        private IDbContextTransaction _dbTransaction;

        public UnitOfWork(TDbContext context, ICapPublisher capPublisher = null)
        {
            _dbContext = context ?? throw new ArgumentNullException(nameof(context));
            _capPublisher = capPublisher;
        }

        /// <summary>
        /// 启用事物
        /// </summary>
        /// <param name="isolationLevel">隔离等级</param>
        public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.RepeatableRead)
        {
            _dbTransaction = _dbContext.Database.BeginTransaction(isolationLevel);
        }

        /// <summary>
        /// 启用事物
        /// </summary>
        /// <param name="sharedToCap">CAP</param>
        /// <exception cref="ArgumentException"></exception>
        public void BeginTransaction(bool sharedToCap = false)
        {
            if (sharedToCap)
            {
                if (_capPublisher == null)
                    throw new ArgumentException("CapPublisher is null");
                else
                    //_dbTransaction= CapTransactionExtensions.BeginTransaction(_dbContext.Database,_capPublisher, true);
                    _dbTransaction = _dbContext.Database.BeginTransaction(this._capPublisher, autoCommit: true);
            }
        }

        /// <summary>
        /// 提交
        /// </summary>
        public void Commit()
        {
            CheckNotNull(_dbTransaction);

            _dbTransaction.Commit();
        }

        /// <summary>
        /// 回滚
        /// </summary>
        public void Rollback()
        {
            CheckNotNull(_dbTransaction);

            _dbTransaction.Rollback();
        }

        /// <summary>
        /// 释放
        /// </summary>
        public void Dispose()
        {
            var isNotNull = CheckNotNull(_dbTransaction, false);
            if (isNotNull)
            {
                _dbTransaction.Dispose();
            }
        }

        /// <summary>
        /// 异步提交
        /// </summary>
        /// <param name="cancellationToken">取消标记</param>
        /// <returns></returns>
        public async Task CommitAsync(CancellationToken cancellationToken = default)
        {
            CheckNotNull(_dbTransaction);

            await _dbTransaction.CommitAsync(cancellationToken);
        }

        /// <summary>
        /// 异步回滚
        /// </summary>
        /// <param name="cancellationToken">取消标记</param>
        /// <returns></returns>
        public async Task RollbackAsync(CancellationToken cancellationToken = default)
        {
            CheckNotNull(_dbTransaction);

            await _dbTransaction.RollbackAsync(cancellationToken);
        }

        private static bool CheckNotNull(IDbContextTransaction dbContextTransaction, bool isThrowException = true)
        {
            if (dbContextTransaction == null && isThrowException)
                throw new ArgumentNullException(nameof(dbContextTransaction), "IDbContextTransaction is null");

            return dbContextTransaction != null;
        }
    }
}
